import { useEffect, useState } from "react";

/**
 * HOC高阶组件
 * 高阶组件就是一个组件，它接受另一个组件作为参数，并返回一个新的组件，（如果你学过Vue的话，跟Vue中的二次封装组件有点类似）新的组件可以复用旧组件的逻辑，并可以添加新的功能。常用于类组件中，
 * 虽然目前都是hooks写法会缩小HOC的使用场景
 * 
 * 注意点：
HOC不会修改传入的组件，而是使用组合的方式，通过将原组件包裹在一个容器组件中来实现功能扩展
注意避免多层嵌套，一般HOC的嵌套层级不要超过3层
HOC的命名规范：with开头，如withLoading、withAuth等

埋点例子:
 封装一个埋点服务可以根据自己的业务自行扩展
trackType表示发送埋点的组件类型
data表示发送的数据
eventData表示需要统计的用户行为数据

***
navigator.sendBeacon是浏览器提供的一种安全可靠的异步数据传输方式，
适合发送少量数据(64kb内），比如埋点数据,并且浏览器关闭时，数据也会发送，
不会阻塞页面加载，没有响应，自动判断数据类型，返回值true表示数据发送成功，false表示数据发送失败
 */

// 负责组装发送数据
const trackService = {
  sendEvent: <T,>(trackType: string, data: T = null as T) => {
    const eventData = {
      timeStamp: Date.now(),
      trackType, // 事件类型
      data, // 事件数据
      userAgent: navigator.userAgent,
      url: window.location.href,
    };

    console.log(eventData);
    // 发送埋点数据
    navigator.sendBeacon("/track", JSON.stringify(eventData));
  },
};

/**
 * 实现HOC高阶组件,通过useEffect统计组件挂载和卸载，并且封装一个trackEvent方法，传递给子组件，子组件可以自行调用，统计用户行为
 * @param WrappedComponent
 * @returns
 */
const withTrack = (WrappedComponent: React.FC, trackType: string) => {
  return (props: any) => {
    // 组件挂载和卸载，发送埋点
    useEffect(() => {
      console.log("埋点");
      trackService.sendEvent(trackType + "mounted", {
        text: "组件挂载",
      });

      return () => {
        trackService.sendEvent(trackType + "unmounted", {
          text: "组件卸载",
        });
      };
    }, []);

    // 封装一个trackEvent方法，传递给子组件，子组件可以自行调用，统计用户行为
    const trackEvent = (eventType: string, data: any) => {
      trackService.sendEvent(trackType + "-" + eventType, data);
    };

    // 这里是相当于子组件，将trackEvent方法传递给子组件
    return <WrappedComponent {...props} trackEvent={trackEvent} />;
  };
};

// 以button组件为例，使用HOC高阶组件
const Button = (props: any) => {
  const { trackEvent } = props;

  const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
    trackEvent(e.type, {
      text: "按钮点击",
      name: e.type,
      clientX: e.clientX,
      clientY: e.clientY,
    });
  };

  return <button onClick={handleClick}>按钮，点击发送数据</button>;
};

const TrackButton = withTrack(Button, "button");

// 使用
export default function HocDemo() {
  return (
    <div>
      <h1>HOC高阶组件</h1>
      <TrackButton />
    </div>
  );
}
